#! /usr/bin/env python
#coding=utf-8

import os
from stat import *

from utils import command
from symbols import UndefinedSymbol
from symbols import ProvidedSymbol

class ElfFile(dict):
	def __init__(self, file, prefix):
		self._f = file
		self._f_safe = "'%s'" % file

		self["name"] = os.path.basename(file)
		self["size"] = os.stat(self._f)[ST_SIZE]
		if self["name"].find(".so") > 0:
			self["type"] = "lib"
		else:
			self["type"] = "bin"
		self["path"] = file[len(prefix):]
		self.__extract_elf_size()
		self.__mklibs_bin = os.path.join(os.path.dirname(os.path.realpath(__file__)), "mklibs-readelf")

	def mklibs_command(self, *args):
		return command(self.__mklibs_bin, ' '.join(args))

	def __eq__(self, other):
		if not isinstance(other, ElfFile):
			return NotImplemented

		return self["path"] == other["path"]#and self["name"] == other["name"]

	def __extract_soname(self):
		soname_data = command("mklibs-readelf", "--print-soname", self._f_safe)
		if soname_data:
			return soname_data.pop()
		return ""

	def __extract_elf_size(self):
		size_data = command("size", self._f_safe)
		if not size_data or len(size_data) < 2:
			self["text_size"] = 0
			self["data_size"] = 0
			self["bss_size"] = 0
			return 0

		vals = size_data[1].split()
		self["text_size"] = int(vals[0])
		self["data_size"] = int(vals[1])
		self["bss_size"] = int(vals[2])
		return ""

	def is_library(self):
		if self["name"].find(".so") > 0:
			return True
		return False

	def get_file(self):
		return self._f

	# Return a set of symbols provided by a library
	def provided_symbols(self):
		if not os.access(self._f, os.F_OK):
			raise Exception("Cannot find lib" + self._f)
		#library = self.__extract_soname()
		library = self["name"]

		output = self.mklibs_command("--print-symbols-provided", self._f_safe)

		result = []
		idx = 1
		for line in output:
			name, weak_string, version_string, default_version_string = line.split()[:4]

			version = None
			if version_string.lower() not in ('base', 'none'):
				version = version_string

			default_version = False
			if default_version_string.lower() == 'true':
				default_version = True

			if "id" in self:
				sym_uid = (self["id"] << 16) + idx # Makesure symbol id is unique
			else:
				sym_uid = idx

			result.append(ProvidedSymbol(name, version, library, default_version, sym_uid))
			idx = idx + 1

		return result

	# Return undefined symbols in an object as a set of tuples (name, weakness)
	def undefined_symbols(self):
		if not os.access(self._f, os.F_OK):
			raise Exception("Cannot find lib" + self._f)

		output = self.mklibs_command("--print-symbols-undefined", self._f_safe)

		result = []
		for line in output:
			name, weak_string, version_string, library_string = line.split()[:4]

			weak = False
			if weak_string.lower() == 'true':
				weak = True

			version = None
			if version_string.lower() not in ('base', 'none'):
				version = version_string

			library = None
			if library_string.lower() != 'none':
				library = library_string
			else:
				library = self.__extract_soname()
				if "" == library:
					library = os.path.basename(self._f)

			result.append(UndefinedSymbol(name, weak, version, library))

		return result

	# Return a set of libraries the passed objects depend on.
	def library_depends(self):
		if not os.access(self._f, os.F_OK):
			raise Exception("Cannot find lib: " + self._f)
		return self.mklibs_command("--print-needed", self._f_safe)

if __name__ == '__main__':
	import elf_walker

	cnt = 0
	elfFiles = elf_walker.ELFWalker()
	for f in elfFiles.get_elf_files():
		if f.find("libskia_ohos.z.so") < 0:
			continue
		elf = ElfFile(f, elfFiles.get_product_images_path())
		print(f)
		print(elf.provided_symbols())
		print(elf.undefined_symbols())
